#pragma once
#ifndef __MY_BINDING__
#define __MY_BINDING__

#include "../mqcommon/helpper.hpp"

#include <iostream>
#include <string>
#include <cassert>
#include <mutex>
#include <memory>
#include <unordered_map>

namespace NS_Binding
{
    struct Binding
    {
        using Ptr = std::shared_ptr<Binding>;
        std::string exchange_name;
        std::string msgqueue_name;
        std::string binding_key;
        bool durable;

        Binding(const std::string &ename, const std::string &qname, const std::string &key, bool bdurable)
            : exchange_name(ename), msgqueue_name(qname), binding_key(key), durable(bdurable)
        {
        }
        ~Binding() {}
    };

    using MsgQueueBindingMap = std::unordered_map<std::string, Binding::Ptr>;
    using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;

    class BindingPersist
    {
        const std::string create_sql = "create table if not exists binding(\
        exchange_name varchar(32), \
        msg_queue_name varchar(32), \
        binding_key varchar(128));";
        const std::string delete_sql = "drop table binding;";

    public:
        BindingPersist(const std::string &path)
            : _helper(path)
        {
            std::string parentpath = NS_Helper::FileHelper::getParentDirectory(path);
            NS_Helper::FileHelper::createDirectory(parentpath);
            assert(_helper.open());
        }

        void createTable()
        {
            assert(_helper.insert(create_sql));
        }
        void deleteTable()
        {
            assert(_helper.erase(delete_sql));
        }

        bool insert(Binding::Ptr ptr)
        {
            std::stringstream ss;
            ss << "insert into binding values";
            ss << "('" << ptr->exchange_name << "',";
            ss << "'" << ptr->msgqueue_name << "',";
            ss << "'" << ptr->binding_key << "');";

            std::cout << ss.str() << std::endl;

            if (!_helper.insert(ss.str()))
            {
                DLOG("持久化绑定数据失败，执行语句: %s", ss.str().c_str());
                return false;
            }

            return true;
        }
        void erase(const std::string &ename, const std::string &qname)
        {
            std::stringstream ss;
            ss << "delete from binding where exchange_name = ";
            ss << "'" << ename << "' and msg_queue_name = '" << qname << "'";
            if (!_helper.insert(ss.str()))
            {
                DLOG("删除持久化绑定数据失败，执行语句: %s", ss.str().c_str());
            }
        }
        void removeExchangeBindings(const std::string &ename)
        {
            std::stringstream ss;
            ss << "delete from binding where exchange_name = ";
            ss << "'" << ename << "'";
            if (!_helper.insert(ss.str()))
            {
                DLOG("删除交换机相关绑定数据失败，执行语句: %s", ss.str().c_str());
            }
        }
        void removeMsgQueueBindings(const std::string &qname)
        {
            std::stringstream ss;
            ss << "delete from binding where msg_queue_name = ";
            ss << "'" << qname << "'";
            if (!_helper.insert(ss.str()))
            {
                DLOG("删除消息队列相关绑定数据失败，执行语句: %s", ss.str().c_str());
            }
        }
        BindingMap recovery()
        {
            std::stringstream ss;
            ss << "select exchange_name, msg_queue_name, binding_key from binding;";
            BindingMap ret;

            if (!_helper.select(ss.str(), selectCallback, ret))
            {
                DLOG("查询持久化消息队列数据失败，执行语句: %s", ss.str().c_str());
            }

            DLOG("恢复历史绑定信息，绑定信息数量：%d", (int)ret.size());
            return ret;
        }

        void clear()
        {
            std::stringstream ss;
            ss << "delete from binding";
            if (!_helper.erase(ss.str()))
            {
                DLOG("删除所有持久化绑定数据失败，执行语句: %s", ss.str().c_str());
            }
        }

        ~BindingPersist() {}

    private:
        static int selectCallback(void *arg, int col_num, char **col_data, char **col_name)
        {
            BindingMap *ret = static_cast<BindingMap *>(arg);
            auto ptr = std::make_shared<Binding>(col_data[0], col_data[1], col_data[2], true);
            (*ret)[ptr->exchange_name][ptr->msgqueue_name] = ptr;
            DLOG("恢复历史绑定信息，交换机名称：%s，队列名称：%s", ptr->exchange_name.c_str(), ptr->msgqueue_name.c_str());

            return 0;
        }

    private:
    NS_Helper::SQLite3Helper _helper;
    };

    class BindingManager
    {
    public:
        using Ptr = std::shared_ptr<BindingManager>;
        BindingManager(const std::string &pathname) : _persist(pathname)
        {
            _persist.createTable();
            _bindings = _persist.recovery();
        }

        bool addBinding(const std::string &ename, const std::string &qname, const std::string &binding_key, bool durable)
        {
            std::unique_lock<std::mutex> ul(_mtx);

            if ((_bindings.find(ename) != _bindings.end()) && (_bindings[ename].find(qname) != _bindings[ename].end()))
                return true;

            auto ptr = std::make_shared<Binding>(ename, qname, binding_key, durable);

            if(durable)
            {
                if(!_persist.insert(ptr))
                    return false;
            }

            _bindings[ename][qname] = ptr;
            return true;
        }

        void deleteBinding(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> ul(_mtx);

            if ((_bindings.find(ename) != _bindings.end()) && (_bindings[ename].find(qname) != _bindings[ename].end()))
            {
                if (_bindings[ename][qname]->durable)
                {
                    _persist.erase(ename, qname);
                }
                _bindings[ename].erase(qname);
            }
        }

        void deleteExchangeBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> ul(_mtx);

            if ((_bindings.find(ename) != _bindings.end()))
            {
                _persist.removeExchangeBindings(ename);
                _bindings.erase(ename);
            }
        }
        void deleteMsgQueueBindings(const std::string &qname)
        {
            std::unique_lock<std::mutex> ul(_mtx);

            _persist.removeMsgQueueBindings(qname);
            for (auto &[ename, queue_map] : _bindings)
            {
                if (queue_map.find(qname) != queue_map.end())
                {
                    queue_map.erase(qname);
                }
            }
        }

        MsgQueueBindingMap getExcahngeBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> ul(_mtx);
            if (_bindings.find(ename) == _bindings.end())
                return MsgQueueBindingMap();

            return _bindings[ename];
        }

        bool exists(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> ul(_mtx);
            if ((_bindings.find(ename) != _bindings.end()) && (_bindings[ename].find(qname) != _bindings[ename].end()))
                return true;

            return false;
        }

        size_t size()
        {
            std::unique_lock<std::mutex> ul(_mtx);

            size_t total = 0;

            for (auto &[ename, queue_map] : _bindings)
            {
                total += queue_map.size();
            }

            return total;
        }

        void clear()
        {
            std::unique_lock<std::mutex> ul(_mtx);
            _bindings.clear();
            _persist.clear();
        }

        ~BindingManager() {}

    private:
        std::mutex _mtx;
        BindingPersist _persist;
        BindingMap _bindings;
    };

}

#endif